/**
 * Project: TacticaPescaditos
 * File: SteeringBehaviors.java
 */
package org.javahispano.javacup.tacticas_aceptadas.Pescaditos.com.chidotronics.javacup;

import org.javahispano.javacup.modelo.Constantes;

import org.javahispano.javacup.tacticas_aceptadas.Pescaditos.com.chidotronics.javacup.geometrics.Vector2D;


/**
 * @author Carlos Alberto
 * class: SteeringBehaviors
 */
public class SteeringBehaviors {
	


	private TacticaPescaditos miTactica;



	/**
	 * @param tacticaPescaditos
	 * @param player
	 */
	public SteeringBehaviors(TacticaPescaditos tacticaPescaditos,
			Player<?> player) {
		this.miTactica = tacticaPescaditos;
		this.player = player;
		this.steeringForce = new Vector2D();
	}

	private enum BehaviorType
	  {
	    NONE(0x0000),
	    SEEK(0x0001),                          
	    PURSUIT(0x0002),           
	    INTERPOSE(0x0004),
	    INTERCEPT(0x0008);
	    
	    private int value;
	    
	    BehaviorType(int value){
	    	this.value = value;
	    }

		public int toInt() {
			return this.value;
		}
	  };
	  
	
	private Player<?> player;
	

	private Vector2D steeringForce;
	
	private Vector2D target;
	
	private double interposeDist;


	private int flags;
	

	
	
	
	private Vector2D seek(Vector2D target){
		Vector2D desiredVelocity = target.substract(player.getPosition())
									.normalized()
									.multiply(player.getMaxVelocity());
		return desiredVelocity.substract(player.getVelocity());
		
	}
	
	private Vector2D pursuit(Ball ball) {
		
		if (player.getBall().getSpeed() ==0){
			target = ball.getPosition();
			return seek(target);
		}

		Vector2D futurePos = ball.getPredictedPosition();

		if ( futurePos.y < -miTactica.getHeightPitch() ) {
			Vector2D toTarget = ball.getPosition().substract(futurePos);
			double y = -miTactica.getHeightPitch() +1;
			double dy =Math.abs( y -ball.getPosition().y );
			double sf = dy/toTarget.y ;
			double x = sf*toTarget.x;
			
			if (Math.abs(x)<=Constantes.LARGO_ARCO/2 ){			
				target = new Vector2D(ball.getPosition().x-x,-miTactica.getHeightPitch());
			} else {
				target = player.getHomePosition();
			}
		} else {
			Vector2D proj =ball.getTrajectory().projection(player.getPosition()); 
			target = (proj != null)?proj:futurePos;
		}


		return  seek(target);

	}
	

	
	private Vector2D interpose(Ball ball, Vector2D  _target,	double distFromTarget){
		return seek(_target.add( ball.getPosition().
							substract( _target).normalized().multiply(distFromTarget)) );
	}

	private Vector2D intercept(Ball ball){
		target =  ball.getPredictedPosition().substract(ball.getPosition()).divide(2.0).add(10);
		return seek(target);
	}


	private boolean isOn(BehaviorType bt){
		return (flags & bt.toInt()) == bt.toInt();
	}
	

	
	
	public Vector2D calculate(){

		  steeringForce.reset();

		  steeringForce = sumForces();


		  return steeringForce;
		
	}

	private Vector2D sumForces() {
		   Vector2D force = new Vector2D();
		     

		   if (isOn(BehaviorType.SEEK))
		   {
			   force = force.add(seek(target));

		     if (!accumulateForce(steeringForce, force)) {
		    	 return steeringForce;
		     }
		   }



		   Ball ball = miTactica.getBall();
		if (isOn(BehaviorType.PURSUIT))
		   {
		     force = force.add( pursuit(ball ));

		     if (!accumulateForce(steeringForce, force)){
		    	 return steeringForce;
		     }
		   }

		   if (isOn(BehaviorType.INTERPOSE))
		   {
		     force = force.add( interpose(ball, target, interposeDist));

		     if (!accumulateForce(steeringForce, force)){
		    	 return steeringForce;
		     }
		   }
		   
		   if (isOn(BehaviorType.INTERCEPT))
		   {
		     force = force.add( intercept(ball));

		     if (!accumulateForce(steeringForce, force)){
		    	 return steeringForce;
		     }
		   }

		   return steeringForce;
	}
	
	public Vector2D getForce() {
		return steeringForce;
	}

	private boolean accumulateForce(Vector2D sf, Vector2D forceToAdd) {

		  double magnitudeSoFar = sf.length();

		  double magnitudeRemaining = player.getMaxVelocity() - magnitudeSoFar;

		  if (magnitudeRemaining <= 0.0){
			  return false;
		  }

		  double magnitudeToAdd = forceToAdd.length();
		  
		  if (magnitudeToAdd > magnitudeRemaining){
		    magnitudeToAdd = magnitudeRemaining;
		  }

		  Vector2D f = sf.add(forceToAdd.normalized().multiply(magnitudeToAdd) );
		  sf.x = f.x;
		  sf.y = f.y;
		  
		  return true;
		
	}

	public void setTarget(Vector2D target) {
		this.target = target;
		
	}

	/**
	 * @return the target
	 */
	public Vector2D getTarget() {
		return target;
	}



	public void seekOn() {
		flags |= BehaviorType.SEEK.value;
		
	}
	
	public void seekOff() {
		if (isOn(BehaviorType.SEEK)) {
			flags ^= BehaviorType.SEEK.value;
		}
		
	}

	public void pursuitOn() {
		flags |= BehaviorType.PURSUIT.value;
	}

	public void pursuitOff() {
		if (isOn(BehaviorType.PURSUIT)) {
			flags ^= BehaviorType.PURSUIT.value;
		}
	}

	public boolean isPursuitOn() {
		return isOn(BehaviorType.PURSUIT);
	}

	public void interposeOn(double d) {
		flags |= BehaviorType.INTERPOSE.value; interposeDist = d;
		
	}
	
	public void interposeOff() {
		if (isOn(BehaviorType.INTERPOSE)) {
			flags ^= BehaviorType.INTERPOSE.value;
		}
	}
	
	public boolean isInterposeOn() {
		return isOn(BehaviorType.INTERPOSE);
	}
	

	public void interceptOn() {
		flags |= BehaviorType.INTERCEPT.value; 
		
	}
	
	public void interceptOff() {
		if (isOn(BehaviorType.INTERCEPT)) {
			flags ^= BehaviorType.INTERCEPT.value;
		}
	}
	
	public boolean isinterceptOn() {
		return isOn(BehaviorType.INTERCEPT);
	}
}
